home *** CD-ROM | disk | FTP | other *** search
Text File | 2012-03-04 | 43.8 KB | 1,725 lines |
- #!/usr/bin/perl
-
- # Copyright 2009-2011 Ben Hutchings
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
- use strict;
- use warnings;
- use Debconf::Client::ConfModule ':all';
- use FileHandle;
- use POSIX ();
- use UUID;
-
- package DebianKernel::DiskId;
-
- ### utility
-
- sub id_to_path {
- my ($id) = @_;
- $id =~ m|^/|
- or $id =~ s{^(LABEL|UUID)=}{'/dev/disk/by-' . lc($1) . '/'}e
- or die "Could not map id $id to path";
- return $id;
- }
-
- ### /etc/fstab
-
- sub fstab_next {
- # Based on my_getmntent() in mount_mntent.c
-
- my ($file) = @_;
- my $text = <$file>;
- unless (defined($text)) {
- return ();
- }
-
- my $line = $text;
- $line =~ s/\r?\n$//;
- $line =~ s/^[ \t]*//;
- if ($line =~ /^(#|$)/) {
- return ($text);
- } else {
- return ($text,
- map({ s/\\([0-7][0-7][0-7])/chr(oct($1) & 0xff)/eg; $_; }
- split(/[ \t]+/, $line)));
- }
- }
-
- sub fstab_list {
- my ($file) = @_;
- my @bdevs;
- while (1) {
- my ($text, $bdev) = fstab_next($file);
- last unless defined($text);
- if (defined($bdev)) {
- push @bdevs, $bdev;
- }
- }
- return @bdevs;
- }
-
- sub fstab_update {
- my ($old, $new, $map) = @_;
- while (1) {
- my ($text, $bdev) = fstab_next($old);
- last unless defined($text);
- if (defined($bdev) && defined(my $id = $map->{$bdev})) {
- $text =~ s/^(\s*\S+)(.*)/# $1$2\n$id$2/;
- }
- $new->print("$text");
- }
- }
-
- ### Kernel parameters
-
- sub kernel_list {
- my ($cmd_line) = @_;
- return ($cmd_line =~ /\broot=(\S+)/) ? ($1) : ();
- }
-
- sub kernel_update {
- my ($cmd_line, $map) = @_;
- if ($cmd_line =~ /\broot=(\S+)/ && defined(my $id = $map->{$1})) {
- $cmd_line =~ s/\broot=(\S+)/root=$id/;
- return $cmd_line;
- } else {
- return undef;
- }
- }
-
- ### shell script variable assignment
-
- # Maintains enough context to find statement boundaries, and can parse
- # variable definitions that do not include substitutions. I think.
-
- sub shellvars_next {
- my ($file) = @_;
- my $text = '';
- my @context = ('');
- my $first = 1;
- my $in_value = 0;
- my ($name, $value);
- my $unhandled = 0;
-
- LINE:
- while (<$file>) {
- $text .= $_;
-
- # variable assignment
- if ($first && m/^\s*([A-Za-z_][A-Za-z0-9_]*)=/g) {
- $name = $1;
- $value = '';
- $in_value = 1;
- }
-
- while (/\G(.*?)([#`'"(){}\s]|\\.|\$[({]?)/gs) {
- my $end_pos = pos;
- my $special = $2;
-
- if ($in_value) {
- # add non-special characters to the value verbatim
- $value .= $1;
- }
-
- if ($context[$#context] eq '') {
- # space outside quotes or brackets ends the value
- if ($special =~ /^\s/) {
- $in_value = 0;
- if ($special eq "\n") {
- last LINE;
- }
- }
- # something else after the value means this is a command
- # with an environment override, not a variable definition
- elsif (defined($name) && !$in_value) {
- $unhandled = 1;
- }
- }
-
- # in single-quoted string
- if ($context[$#context] eq "'") {
- # only the terminating single-quote is special
- if ($special eq "'") {
- pop @context;
- } else {
- $value .= $special;
- }
- }
- # backslash escape
- elsif ($special =~ /^\\/) {
- if ($in_value && $special ne "\\\n") {
- $value .= substr($special, 1, 1);
- }
- }
- # in backtick substitution
- elsif ($context[$#context] eq '`') {
- # backtick does not participate in nesting, so only the
- # terminating backtick should be considered special
- if ($special eq '`') {
- pop @context;
- }
- }
- # comment
- elsif ($context[$#context] !~ /^['"]/ && $special eq '#') {
- # ignore rest of the physical line, except the new-line
- pos = $end_pos;
- /\G.*/g;
- next;
- }
- # start of backtick substitution
- elsif ($special eq '`') {
- push @context, '`';
- $unhandled = 1;
- }
- # start of single/double-quoted string
- elsif ($special =~ /^['"]/ && $context[$#context] !~ /^['"]/) {
- push @context, $special;
- }
- # end of double-quoted string
- elsif ($special eq '"' && $context[$#context] eq '"') {
- pop @context;
- }
- # open bracket
- elsif ($special =~ /^\$?\(/) {
- push @context, ')';
- $unhandled = 1;
- } elsif ($special =~ /^\$\{/) {
- push @context, '}';
- $unhandled = 1;
- }
- # close bracket
- elsif ($special =~ /^[)}]/ && $special eq $context[$#context]) {
- pop @context;
- }
- # variable substitution
- elsif ($special eq '$') {
- $unhandled = 1;
- }
- # not a special character in this context (or a syntax error)
- else {
- if ($in_value) {
- $value .= $special;
- }
- }
-
- pos = $end_pos;
- }
-
- $first = 0;
- }
-
- if ($text eq '') {
- return ();
- } elsif ($unhandled) {
- return ($text);
- } else {
- return ($text, $name, $value);
- }
- }
-
- sub shellvars_quote {
- my ($value) = @_;
- $value =~ s/'/'\''/g;
- return "'$value'";
- }
-
- ### GRUB 1 (grub-legacy) config
-
- sub grub1_path {
- for ('/boot/grub', '/boot/boot/grub') {
- if (-d) {
- return "$_/menu.lst";
- }
- }
- return undef;
- }
-
- sub grub1_parse {
- my ($file) = @_;
- my @results = ();
- my $text = '';
- my $in_auto = 0;
- my $in_opts = 0;
-
- while (<$file>) {
- if ($in_opts && /^\# (\w+)=(.*)/) {
- push @results, [$text];
- $text = '';
- push @results, [$_, $1, $2];
- } else {
- $text .= $_;
- if ($_ eq "### BEGIN AUTOMAGIC KERNELS LIST\n") {
- $in_auto = 1;
- } elsif ($_ eq "### END DEBIAN AUTOMAGIC KERNELS LIST\n") {
- $in_auto = 0;
- } elsif ($_ eq "## ## Start Default Options ##\n") {
- $in_opts = $in_auto;
- } elsif ($_ eq "## ## End Default Options ##\n") {
- $in_opts = 0;
- }
- }
- }
-
- if ($text ne '') {
- push @results, [$text];
- }
-
- return @results;
- }
-
- sub grub1_list {
- my ($file) = @_;
- my %options;
- for (grub1_parse($file)) {
- my ($text, $name, $value) = @$_;
- next unless defined($name);
- $options{$name} = $value;
- }
-
- my @bdevs;
- if (exists($options{kopt_2_6})) {
- push @bdevs, kernel_list($options{kopt_2_6});
- } elsif (exists($options{kopt})) {
- push @bdevs, kernel_list($options{kopt});
- }
- if (exists($options{xenkopt})) {
- push @bdevs, kernel_list($options{xenkopt});
- }
- return @bdevs;
- }
-
- sub grub1_update {
- my ($old, $new, $map) = @_;
-
- my %options;
- for (grub1_parse($old)) {
- my ($text, $name, $value) = @$_;
- next unless defined($name);
- $options{$name} = $value;
- }
-
- $old->seek(0, 0);
- for (grub1_parse($old)) {
- my ($text, $name, $value) = @$_;
- if (defined($name) &&
- ($name eq 'kopt_2_6' ||
- ($name eq 'kopt' && !exists($options{kopt_2_6})) ||
- $name eq 'xenkopt')) {
- if (defined(my $new_value = kernel_update($value, $map))) {
- $text = "## $name=$value\n# $name=$new_value\n";
- }
- }
- $new->print($text);
- }
- }
-
- sub grub1_post {
- system('update-grub');
- }
-
- ### GRUB 2 config
-
- sub grub2_list {
- my ($file) = @_;
- my @bdevs;
-
- while (1) {
- my ($text, $name, $value) = shellvars_next($file);
- last unless defined($text);
- if (defined($name) && $name =~ /^GRUB_CMDLINE_LINUX(?:_DEFAULT)?$/) {
- push @bdevs, kernel_list($value);
- }
- }
-
- return @bdevs;
- }
-
- sub grub2_update {
- my ($old, $new, $map) = @_;
- my @bdevs;
-
- while (1) {
- my ($text, $name, $value) = shellvars_next($old);
- last unless defined($text);
- if (defined($name) && $name =~ /^GRUB_CMDLINE_LINUX(?:_DEFAULT)?$/ &&
- defined(my $new_value = kernel_update($value, $map))) {
- $text =~ s/^/# /gm;
- $text .= sprintf("%s=%s\n", $name, shellvars_quote($new_value));
- }
- $new->print($text);
- }
- }
-
- sub grub2_post {
- system('grub-mkconfig', '-o', '/boot/grub/grub.cfg');
- }
-
- ### LILO
-
- sub lilo_tokenize {
- # Based on cfg_get_token() and next() in cfg.c.
- # Line boundaries are *not* significant (except as white space) so
- # we tokenize the whole file at once.
-
- my ($file) = @_;
- my @tokens = ();
- my $text = '';
- my $token;
- my $in_quote = 0;
-
- while (<$file>) {
- # If this is the continuation of a multi-line quote, skip
- # leading space and push back the necessary context.
- if ($in_quote) {
- s/^[ \t]*/"/;
- $text .= $&;
- }
-
- pos = 0;
- while (/\G \s* (?:\#.*)?
- (?: (=) |
- " ((?:[^"] | \\[\\"n])*) (" | \\\r?\n) |
- ((?:[^\s\#="\\] | \\[^\r\n])+) (\\\r?\n)?)?
- /gsx) {
- my $cont;
- my $new_text = $&;
-
- if (defined($1)) {
- # equals sign
- $text = $new_text;
- $token = $1;
- $cont = 0;
- } elsif (defined($2)) {
- # quoted text
- if (!$in_quote) {
- $text = $new_text;
- $token = $2;
- } else {
- $text .= substr($new_text, 1); # remove the quote again; ick
- $token .= ' ' . $2;
- }
- $cont = $3 ne '"';
- } elsif (defined($4)) {
- # unquoted word
- if (!defined($token)) {
- $token = '';
- }
- $text .= $new_text;
- $token .= $4;
- $cont = defined($5);
- } else {
- $text .= $new_text;
- $cont = $new_text eq '';
- }
-
- if (!$cont) {
- if ($text =~ /(?:^|[^\\])\$/) {
- # unhandled expansion
- $token = undef;
- } elsif (defined($token)) {
- if ($in_quote) {
- $token =~ s/\\(.)/$1 eq 'n' ? "\n" : $1/eg;
- } else {
- $token =~ s/\\(.)/$1/g;
- }
- }
- push @tokens, [$text, $token];
- $text = '';
- $token = undef;
- $in_quote = 0;
- }
- }
- }
-
- return @tokens;
- }
-
- sub lilo_list {
- my ($file) = @_;
- my @bdevs = ();
- my @tokens = lilo_tokenize($file);
- my $i = 0;
- my $in_generic = 1; # global or image=/vmlinuz or image=/vmlinuz.old
-
- while ($i <= $#tokens) {
- # Configuration items are either <name> "=" <value> or <name> alone.
- if ($#tokens - $i >= 2 &&
- defined($tokens[$i + 1][1]) && $tokens[$i + 1][1] eq '=') {
- my ($name, $value) = ($tokens[$i][1], $tokens[$i + 2][1]);
- if (defined($name) && defined($value)) {
- if ($name eq 'image') {
- $in_generic = ($value =~ m|^/vmlinuz(?:\.old)?$|);
- } elsif ($in_generic) {
- if ($name =~ /^(?:boot|root)$/) {
- push @bdevs, $value;
- } elsif ($name =~ /^(?:addappend|append|literal)$/) {
- push @bdevs, kernel_list($value);
- }
- }
- }
- $i += 3;
- } else {
- $i += 1;
- }
- }
-
- return @bdevs;
- }
-
- sub _lilo_update {
- my ($old, $new, $map, $replace) = @_;
- my @tokens = lilo_tokenize($old);
- my $i = 0;
- my $in_generic = 1; # global or image=/vmlinuz or image=/vmlinuz.old
-
- while ($i <= $#tokens) {
- my $text = $tokens[$i][0];
-
- if ($#tokens - $i >= 2 &&
- defined($tokens[$i + 1][1]) && $tokens[$i + 1][1] eq '=') {
- my ($name, $value) = ($tokens[$i][1], $tokens[$i + 2][1]);
- my $new_value;
- if (defined($name) && defined($value)) {
- if ($name eq 'image') {
- $in_generic = ($value =~ m|^/vmlinuz(?:\.old)?$|);
- } elsif ($in_generic) {
- if ($name eq 'boot') {
- # 'boot' is used directly by the lilo command, which
- # doesn't use libblkid
- $new_value = $map->{$value} && id_to_path($map->{$value});
- } elsif ($name eq 'root') {
- # 'root' adds a root parameter to the kernel command
- # line
- $new_value = $map->{$value};
- } elsif ($name =~ /^(?:addappend|append|literal)$/) {
- # These are all destined for the kernel command line
- # in some way
- $new_value = kernel_update($value, $map);
- }
- }
- }
- if (defined($new_value)) {
- $new_value =~ s/\\/\\\\/g;
- $text = &{$replace}($name, $value, $new_value) ||
- "\n# $name = $value\n$name = \"$new_value\"\n";
- } else {
- $text .= $tokens[$i + 1][0] . $tokens[$i + 2][0];
- }
- $i += 3;
- } else {
- $i += 1;
- }
-
- $new->print($text);
- }
- }
-
- sub lilo_update {
- my ($old, $new, $map) = @_;
- _lilo_update($old, $new, $map, sub { return undef });
- }
-
- sub lilo_post {
- system('lilo');
- }
-
- ### SILO
-
- sub silo_post {
- system('silo');
- }
-
- ### Yaboot
-
- sub yaboot_post {
- system('ybin');
- }
-
- ### ELILO
-
- sub elilo_update {
- my ($old, $new, $map) = @_;
- # Work around bug #581173 - boot value must have no space before
- # and no quotes around it.
- sub replace {
- my ($name, $value, $new_value) = @_;
- return ($name eq 'boot') ? "# boot=$value\nboot=$new_value\n" : undef;
- }
- _lilo_update($old, $new, $map, \&replace);
- }
-
- sub elilo_post {
- system('elilo');
- }
-
- ### extlinux
-
- sub extlinux_old_path {
- for ('/boot/extlinux', '/boot/boot/exlinux', '/extlinux') {
- if (-e) {
- return "$_/options.cfg";
- }
- }
- return undef;
- }
-
- sub extlinux_old_list {
- my ($file) = @_;
- while (<$file>) {
- if (/^## ROOT=(.*)/) {
- return kernel_list($1);
- }
- }
- return ();
- }
-
- sub extlinux_old_update {
- my ($old, $new, $map) = @_;
- while (<$old>) {
- my $text = $_;
- if (/^## ROOT=(.*)/) {
- my $new_params = kernel_update($1, $map);
- if (defined($new_params)) {
- $text = "## $text" . "## ROOT=$new_params\n";
- }
- }
- $new->print($text);
- }
- }
-
- sub extlinux_new_list {
- my ($file) = @_;
- while (<$file>) {
- if (/^# ROOT=(.*)/) {
- return kernel_list($1);
- }
- }
- return ();
- }
-
- sub extlinux_new_update {
- my ($old, $new, $map) = @_;
- while (<$old>) {
- my $text = $_;
- if (/^# ROOT=(.*)/) {
- my $new_params = kernel_update($1, $map);
- if (defined($new_params)) {
- $text = "## $text" . "# ROOT=$new_params\n";
- }
- }
- $new->print($text);
- }
- }
-
- sub extlinux_post {
- system('update-extlinux');
- }
-
- # udev persistent-cd
-
- sub udev_next {
- my ($file) = @_;
- my @results = ();
-
- # Based on parse_file() and get_key() in udev-rules.c
- while (1) {
- my $text = <$file>;
- last if !defined($text) || $text eq '';
-
- if ($text =~ /^\s*(?:#|$)/) {
- push @results, [$text];
- } else {
- my $end_pos = 0;
- while ($text =~ /\G [\s,]* ((?:[^\s=+!:]|[+!:](?!=))+)
- \s* ([=+!:]?=) "([^"]*)"/gx) {
- push @results, [$&, $1, $2, $3];
- $end_pos = pos($text);
- }
- push @results, [substr($text, $end_pos)];
- last if $text !~ /\\\n$/;
- }
- }
-
- return @results;
- }
-
- sub udev_parse_symlink_rule {
- my ($path, $symlink);
- for (@_) {
- my ($text, $key, $op, $value) = @$_;
- next if !defined($key);
- if ($key eq 'ENV{ID_PATH}' && $op eq '==') {
- $path = $value;
- } elsif ($key eq 'SYMLINK' && $op eq '+=') {
- $symlink = $value;
- }
- }
- return ($path, $symlink);
- }
-
- # Find symlink rules using IDE device paths that aren't matched by rules
- # using the corresponding SCSI device path. Return an array containing
- # the corresponding path for each rule where this is the case and undef
- # for all other rules.
- sub udev_cd_find_unmatched_ide_rules {
- my ($file) = @_;
- my %wanted_rule;
- my @unmatched;
- my $i = 0;
-
- while (1) {
- my @keys = udev_next($file);
- last if $#keys < 0;
-
- my ($path, $symlink) = udev_parse_symlink_rule(@keys);
- if (defined($path) && defined($symlink)) {
- if ($path =~ /-ide-\d+:\d+$/) {
- # libata uses the PATA controller and device numbers
- # as SCSI host number and bus id. Channel number and
- # LUN are always 0. The parent device path should
- # stay the same.
- $path =~ s/-ide-(\d+):(\d+)$/-scsi-$1:0:$2:0/;
- my $rule_key = $path . ' ' . $symlink;
- if (!exists($wanted_rule{$rule_key})) {
- $wanted_rule{$rule_key} = $i;
- $unmatched[$i] = $path;
- }
- } elsif ($path =~ /-scsi-\d+:\d+:\d+:\d+$/) {
- my $rule_key = $path . ' ' . $symlink;
- my $j = $wanted_rule{$rule_key};
- if (defined($j) && $j >= 0) {
- $unmatched[$j] = undef;
- }
- $wanted_rule{$rule_key} = -1;
- }
- }
-
- ++$i;
- }
-
- return @unmatched;
- }
-
- sub udev_cd_needs_update {
- my ($file) = @_;
- my %paths;
- for (udev_cd_find_unmatched_ide_rules($file)) {
- if (defined($_)) {
- $paths{$_} = 1;
- }
- }
- return join('\n', map({"+ PATH=$_"} keys(%paths)));
- }
-
- sub udev_cd_update {
- my ($old, $new) = @_; # ignore map
-
- # Find which rules we will need to copy and edit, then rewind
- my @unmatched = udev_cd_find_unmatched_ide_rules($old);
- $old->seek(0, 0);
-
- my $i = 0;
- while (1) {
- my @keys = udev_next($old);
- last if $#keys < 0;
-
- my $old_text = '';
- my $new_text = '';
-
- for (@keys) {
- my ($text, $key, $op, $value) = @$_;
- $old_text .= $text;
- next unless defined($unmatched[$i]) && defined($key);
-
- if ($key eq 'ENV{ID_PATH}' && $op eq '==') {
- my $value = $unmatched[$i];
- $new_text .= ", $key$op\"$value\"";
- } else {
- $new_text .= $text;
- }
- }
-
- $new->print($old_text);
- if ($unmatched[$i]) {
- $new->print($new_text . "\n");
- }
-
- ++$i;
- }
- }
-
- # initramfs-tools resume
-
- sub initramfs_resume_list {
- my ($file) = @_;
- my @results = ();
-
- while (1) {
- my ($text, $name, $value) = shellvars_next($file);
- last unless defined($text);
- if (defined($name) && $name eq 'RESUME') {
- $results[0] = $value;
- }
- }
-
- return @results;
- }
-
- sub initramfs_resume_update {
- my ($old, $new, $map) = @_;
-
- while (1) {
- my ($text, $name, $value) = shellvars_next($old);
- last unless defined($text);
- if (defined($name) && $name eq 'RESUME' &&
- defined(my $new_value = $map->{$value})) {
- $text =~ s/^/# /gm;
- $text .= sprintf("%s=%s\n", $name, shellvars_quote($new_value));
- }
- $new->print($text);
- }
- }
-
- # uswsusp resume
-
- sub uswsusp_next {
- # Based on parse_line() in config_parser.c
-
- my ($file) = @_;
- my $text = <$file>;
-
- if (!defined($text) || $text eq '') {
- return ();
- }
-
- local $_ = $text;
- s/^\s*(?:#.*)?//;
- s/\s*$//;
-
- if ($text =~ /^([\w ]*\w)[ \t]*[:=][ \t]*(.+)$/) {
- return ($text, $1, $2);
- } else {
- return ($text);
- }
- }
-
- sub uswsusp_resume_list {
- my ($file) = @_;
- my @results = ();
-
- while (1) {
- my ($text, $name, $value) = uswsusp_next($file);
- last unless defined($text);
- if (defined($name) && $name eq 'resume device') {
- $results[0] = $value;
- }
- }
-
- return @results;
- }
-
- sub uswsusp_resume_update {
- my ($old, $new, $map) = @_;
-
- while (1) {
- my ($text, $name, $value) = uswsusp_next($old);
- last unless defined($text);
- if (defined($name) && $name eq 'resume device' &&
- defined(my $new_value = $map->{$value})) {
- $text =~ s/^/# /gm;
- $text .= sprintf("%s = %s\n", $name, id_to_path($new_value));
- }
- $new->print($text);
- }
- }
-
- # cryptsetup
-
- sub cryptsetup_next {
- my ($file) = @_;
- my $text = <$file>;
- unless (defined($text)) {
- return ();
- }
-
- my $line = $text;
- if ($line =~ /^\s*(#|$)/) {
- return ($text);
- } else {
- $line =~ s/\s*$//;
- $line =~ s/^\s*//;
- return ($text, split(/\s+/, $line, 4));
- }
- }
-
- sub cryptsetup_list {
- my ($file) = @_;
- my (@results) = ();
-
- while (1) {
- my ($text, undef, $src) = cryptsetup_next($file);
- last unless defined($text);
- if (defined($src)) {
- push @results, $src;
- }
- }
-
- return @results;
- }
-
- sub cryptsetup_update {
- my ($old, $new, $map) = @_;
-
- while (1) {
- my ($text, $dst, $src, $key, $opts) = cryptsetup_next($old);
- last unless defined($text);
- if (defined($src) && defined($map->{$src})) {
- $text = "# $text" .
- join(' ', $dst, $map->{$src}, $key, $opts) . "\n";
- }
- $new->print($text);
- }
- }
-
- # hdparm
-
- sub hdparm_list {
- my ($file) = @_;
- my (@results) = ();
-
- # I really can't be bothered to parse this mess. Just see if
- # there's anything like a device name on a non-comment line.
- while (<$file>) {
- if (!/^\s*#/) {
- push @results, grep({m|^/dev/|} split(/\s+/));
- }
- }
-
- return @results;
- }
-
- ### mdadm
-
- sub mdadm_list {
- my ($file) = @_;
- my (@results) = ();
-
- while (<$file>) {
- # Look for DEVICE (case-insensitive, may be abbreviated to as
- # little as 3 letters) followed by a whitespace-separated list
- # of devices (or wildcards, or keywords!). Ignore comments
- # (hash preceded by whitespace).
- if (/^DEV(?:I(?:C(?:E)?)?)?[ \t]*((?:[^ \t]|[ \t][^#])*)/i) {
- push @results, split(/[ \t]+/, $1);
- }
- }
-
- return @results;
- }
-
- ### list of all configuration files and functions
-
- my @config_files = ({packages => 'mount',
- path => '/etc/fstab',
- list => \&fstab_list,
- update => \&fstab_update},
- {packages => 'grub grub-legacy',
- path => grub1_path(),
- list => \&grub1_list,
- update => \&grub1_update,
- post_update => \&grub1_post,
- is_boot_loader => 1},
- {packages => 'grub-common',
- path => '/etc/default/grub',
- list => \&grub2_list,
- update => \&grub2_update,
- post_update => \&grub2_post,
- is_boot_loader => 1},
- {packages => 'lilo',
- path => '/etc/lilo.conf',
- list => \&lilo_list,
- update => \&lilo_update,
- post_update => \&lilo_post,
- is_boot_loader => 1},
- {packages => 'silo',
- path => '/etc/silo.conf',
- list => \&lilo_list,
- update => \&lilo_update,
- post_update => \&silo_post,
- is_boot_loader => 1},
- {packages => 'quik',
- path => '/etc/quik.conf',
- list => \&lilo_list,
- update => \&lilo_update,
- is_boot_loader => 1},
- {packages => 'yaboot',
- path => '/etc/yaboot.conf',
- list => \&lilo_list,
- update => \&lilo_update,
- post_update => \&yaboot_post,
- is_boot_loader => 1},
- {packages => 'elilo',
- path => '/etc/elilo.conf',
- list => \&lilo_list,
- update => \&elilo_update,
- post_update => \&elilo_post,
- is_boot_loader => 1},
- {packages => 'extlinux',
- path => extlinux_old_path(),
- list => \&extlinux_old_list,
- update => \&extlinux_old_update,
- post_update => \&extlinux_post,
- is_boot_loader => 1},
- {packages => 'extlinux',
- path => '/etc/default/extlinux',
- list => \&extlinux_new_list,
- update => \&extlinux_new_update,
- post_update => \&extlinux_post,
- is_boot_loader => 1},
- {packages => 'udev',
- path => '/etc/udev/rules.d/70-persistent-cd.rules',
- needs_update => \&udev_cd_needs_update,
- update => \&udev_cd_update},
- {packages => 'initramfs-tools',
- path => '/etc/initramfs-tools/conf.d/resume',
- list => \&initramfs_resume_list,
- update => \&initramfs_resume_update,
- # udev will source all files in this directory,
- # with few exceptions. Such as including a '^'.
- suffix => '^old'},
- {packages => 'uswsusp',
- path => '/etc/uswsusp.conf',
- list => \&uswsusp_resume_list,
- update => \&uswsusp_resume_update},
- {packages => 'cryptsetup',
- path => '/etc/crypttab',
- list => \&cryptsetup_list,
- update => \&cryptsetup_update},
- # mdadm.conf requires manual update because it may
- # contain wildcards.
- {packages => 'mdadm',
- path => '/etc/mdadm/mdadm.conf',
- list => \&mdadm_list},
- # hdparm.conf requires manual update because it
- # (1) refers to whole disks (2) might not work
- # properly with the new drivers (3) is in a very
- # special format.
- {packages => 'hdparm',
- path => '/etc/hdparm.conf',
- list => \&hdparm_list});
-
- ### Filesystem labels and UUIDs
-
- sub ext2_set_label {
- my ($bdev, $label) = @_;
- system('tune2fs', '-L', $label, $bdev) == 0 or die "tune2fs failed: $?";
- }
- sub ext2_set_uuid {
- my ($bdev, $uuid) = @_;
- system('tune2fs', '-U', $uuid, $bdev) == 0 or die "tune2fs failed: $?";
- }
-
- sub jfs_set_label {
- my ($bdev, $label) = @_;
- system('jfs_tune', '-L', $label, $bdev) == 0 or die "jfs_tune failed: $?";
- }
- sub jfs_set_uuid {
- my ($bdev, $uuid) = @_;
- system('jfs_tune', '-U', $uuid, $bdev) == 0 or die "jfs_tune failed: $?";
- }
-
- sub fat_set_label {
- my ($bdev, $label) = @_;
- system('dosfslabel', $bdev, $label) == 0 or die "dosfslabel failed: $?";
- }
-
- sub ntfs_set_label {
- my ($bdev, $label) = @_;
- system('ntfslabel', $bdev, $label) == 0 or die "ntfslabel failed: $?";
- }
-
- sub reiserfs_set_label {
- my ($bdev, $label) = @_;
- system('reiserfstune', '--label', $label, $bdev)
- or die "reiserfstune failed: $?";
- }
- sub reiserfs_set_uuid {
- my ($bdev, $uuid) = @_;
- system('reiserfstune', '--uuid', $uuid, $bdev)
- or die "reiserfstune failed: $?";
- }
-
- # There is no command to relabel swap, and we mustn't run mkswap if
- # the partition is already in use. Thankfully the header format is
- # pretty simple; it starts with this structure:
- # struct swap_header_v1_2 {
- # char bootbits[1024]; /* Space for disklabel etc. */
- # unsigned int version;
- # unsigned int last_page;
- # unsigned int nr_badpages;
- # unsigned char uuid[16];
- # char volume_name[16];
- # unsigned int padding[117];
- # unsigned int badpages[1];
- # };
- # and has the signature 'SWAPSPACE2' at the end of the first page.
- use constant { SWAP_SIGNATURE => 'SWAPSPACE2',
- SWAP_UUID_OFFSET => 1036, SWAP_UUID_LEN => 16,
- SWAP_LABEL_OFFSET => 1052, SWAP_LABEL_LEN => 16 };
- sub _swap_set_field {
- my ($bdev, $offset, $value) = @_;
- my $pagesize = POSIX::sysconf(POSIX::_SC_PAGESIZE) or die "$!";
- my ($length, $signature);
-
- my $fd = POSIX::open($bdev, POSIX::O_RDWR);
- defined($fd) or die "$!";
-
- # Check the signature
- POSIX::lseek($fd, $pagesize - length(SWAP_SIGNATURE), POSIX::SEEK_SET);
- $length = POSIX::read($fd, $signature, length(SWAP_SIGNATURE));
- if (!defined($length) || $signature ne SWAP_SIGNATURE) {
- POSIX::close($fd);
- die "swap signature not found on $bdev";
- }
-
- # Set the field
- POSIX::lseek($fd, $offset, POSIX::SEEK_SET);
- $length = POSIX::write($fd, $value, length($value));
- if (!defined($length) || $length != length($value)) {
- my $error = "$!";
- POSIX::close($fd);
- die $error;
- }
-
- POSIX::close($fd);
- }
- sub swap_set_label {
- my ($bdev, $label) = @_;
- _swap_set_field($bdev, SWAP_LABEL_OFFSET, pack('Z' . SWAP_LABEL_LEN, $label));
- }
- sub swap_set_uuid {
- my ($bdev, $uuid) = @_;
- my $uuid_bin;
- if (UUID::parse($uuid, $uuid_bin) != 0 ||
- length($uuid_bin) != SWAP_UUID_LEN) {
- die "internal error: invalid UUID string";
- }
- _swap_set_field($bdev, SWAP_UUID_OFFSET, $uuid_bin);
- }
-
- sub ufs_set_label {
- my ($bdev, $label) = @_;
- system('tunefs.ufs', '-L', $label, $bdev) or die "tunefs.ufs failed: $?";
- }
-
- sub xfs_set_label {
- my ($bdev, $label) = @_;
- system('xfs_admin', '-L', $label, $bdev) or die "xfs_admin failed: $?";
- }
- sub xfs_set_uuid {
- my ($bdev, $uuid) = @_;
- system('xfs_admin', '-U', $uuid, $bdev) or die "xfs_admin failed: $?";
- }
-
- my %filesystem_types = (
- ext2 => { label_len => 16, set_label => \&ext2_set_label,
- set_uuid => \&ext2_set_uuid },
- ext3 => { label_len => 16, set_label => \&ext2_set_label,
- set_uuid => \&ext2_set_uuid },
- ext4 => { label_len => 16, set_label => \&ext2_set_label,
- set_uuid => \&ext2_set_uuid },
- jfs => { label_len => 16, set_label => \&jfs_set_label,
- set_uuid => \&jfs_set_uuid },
- msdos => { label_len => 11, set_label => \&fat_set_label },
- ntfs => { label_len => 128, set_label => \&ntfs_set_label },
- reiserfs => { label_len => 16, set_label => \&reiserfs_set_label,
- set_uuid => \&reiserfs_set_uuid },
- swap => { label_len => SWAP_LABEL_LEN, set_label => \&swap_set_label,
- set_uuid => \&swap_set_uuid },
- ufs => { label_len => 32, set_label => \&ufs_set_label },
- vfat => { label_len => 11, set_label => \&fat_set_label },
- xfs => { label_len => 12, set_label => \&xfs_set_label,
- set_uuid => \&xfs_set_uuid }
- );
-
- my %bdev_map;
- my %id_map;
-
- sub scan_config_files {
- my $bdev_regex = shift;
- my @configs;
-
- # Find all matching devices mentioned in configurations
- for my $config (@config_files) {
- # Is the file present?
- my $path = $config->{path};
- if (!defined($path)) {
- next;
- }
- my $file = new FileHandle($path, 'r');
- if (!defined($file)) {
- if ($! == POSIX::ENOENT) {
- next;
- }
- die "$!";
- }
-
- # Are any of the related packages wanted or installed?
- my $wanted = 0;
- my $unpacked = 0;
- my $installed = 0;
- my $packages = $config->{packages};
- for (`dpkg-query 2>/dev/null --showformat '\${status}\\n' -W $packages`)
- {
- $wanted = 1 if /^install /;
- $installed = 1 if / installed\n$/;
- $unpacked = 1 if / (installed|unpacked)\n$/;
- }
- if (!$wanted && !$unpacked) {
- next;
- }
-
- my @matched_bdevs = ();
- my $id_map_text;
- my $needs_update;
-
- if (exists($config->{needs_update})) {
- $id_map_text = &{$config->{needs_update}}($file);
- $needs_update = defined($id_map_text) && $id_map_text ne '';
- } elsif (exists($config->{list})) {
- for my $bdev (&{$config->{list}}($file)) {
- # Check whether the device name matches the given
- # regex. Also check that the device node exists,
- # unless the name is a wildcard.
- if ($bdev =~ $bdev_regex && ($bdev =~ m/[\?\*]/ || -b $bdev)) {
- $bdev_map{$bdev} = {};
- push @matched_bdevs, $bdev;
- }
- }
- $needs_update = @matched_bdevs > 0;
- } else {
- # Needs manual update
- $needs_update = 1;
- }
-
- push @configs, {config => $config,
- devices => \@matched_bdevs,
- id_map_text => $id_map_text,
- installed => $installed,
- unpacked => $unpacked,
- needs_update => $needs_update};
- }
-
- my $fstab = new FileHandle('/etc/fstab', 'r') or die "$!";
- while (1) {
- my ($text, $bdev, $path, $type) = fstab_next($fstab);
- last unless defined($text);
- if (defined($type) && exists($bdev_map{$bdev})) {
- $bdev_map{$bdev}->{path} = $path;
- $bdev_map{$bdev}->{type} = $type;
- }
- }
- $fstab->close();
-
- return @configs;
- }
-
- sub add_tag {
- # Map disks to labels/UUIDs and vice versa. Include all disks in
- # the reverse mapping so we can detect ambiguity.
- my ($bdev, $name, $value, $new) = @_;
- my $id = "$name=$value";
- push @{$id_map{$id}}, $bdev;
- if (exists($bdev_map{$bdev})) {
- $bdev_map{$bdev}->{$name} = $value;
- push @{$bdev_map{$bdev}->{ids}}, $id;
- }
- if ($new) {
- $bdev_map{$bdev}->{new_id} = $id;
- }
- }
-
- sub scan_devices {
- my $id_command;
- if (-x '/sbin/vol_id') {
- $id_command = '/sbin/vol_id';
- } else {
- $id_command = 'blkid -o udev -s LABEL -s UUID -s TYPE';
- }
- for (`blkid -o device`) {
- chomp;
- my $bdev = $_;
- for (`$id_command '$bdev'`) {
- if (/^ID_FS_(LABEL|UUID)_ENC=(.+)\n$/) {
- add_tag($bdev, $1, $2);
- } elsif (/^ID_FS_TYPE=(.+)\n$/ && exists($bdev_map{$bdev})) {
- $bdev_map{$bdev}->{type} //= $1;
- }
- }
- }
-
- # Discard UUIDs for LVM2 PVs, as we assume there are symlinks for all
- # UUIDs under /dev/disk/by-uuid and this is not true for PVs.
- # Discard all labels and UUIDs(!) that are ambiguous.
- # Discard all labels with 'unsafe' characters (escaped by blkid using
- # backslashes) as they will not be usable in all configuration files.
- # Similarly for '#' which blkid surprisingly does not consider unsafe.
- # Sort each device's IDs in reverse lexical order so that UUIDs are
- # preferred.
- for my $bdev (keys(%bdev_map)) {
- if (!defined($bdev_map{$bdev}->{type}) ||
- $bdev_map{$bdev}->{type} eq 'LVM2_member') {
- @{$bdev_map{$bdev}->{ids}} = ();
- } else {
- @{$bdev_map{$bdev}->{ids}} =
- sort({$b cmp $a}
- grep({ @{$id_map{$_}} == 1 && $_ !~ /[\\#]/ }
- @{$bdev_map{$bdev}->{ids}}));
- }
- }
-
- # Add persistent aliases for CD/DVD/BD drives
- my $cd_rules =
- new FileHandle('/etc/udev/rules.d/70-persistent-cd.rules', 'r');
- while (defined($cd_rules)) {
- my @keys = udev_next($cd_rules);
- last if $#keys < 0;
-
- my ($path, $symlink) = udev_parse_symlink_rule(@keys);
- if (defined($path) && defined($symlink)) {
- $symlink =~ s{^(?!/)}{/dev/};
- my $bdev = readlink($symlink) or next;
- $bdev =~ s{^(?!/)}{/dev/};
- if (exists($bdev_map{$bdev})) {
- push @{$bdev_map{$bdev}->{ids}}, $symlink;
- }
- }
- }
- }
-
- sub assign_new_ids {
- my $hostname = (POSIX::uname())[1];
-
- # For all devices that have no alternate device ids, suggest setting
- # UUIDs, labelling them based on fstab or just using a generic label.
- for my $bdev (keys(%bdev_map)) {
- next if $#{$bdev_map{$bdev}->{ids}} >= 0;
-
- my $type = $bdev_map{$bdev}->{type};
- next unless defined($type) && exists($filesystem_types{$type});
-
- if (defined($filesystem_types{$type}->{set_uuid})) {
- my ($uuid_bin, $uuid);
- UUID::generate($uuid_bin);
- UUID::unparse($uuid_bin, $uuid);
- add_tag($bdev, 'UUID', $uuid, 1);
- next;
- }
-
- my $label_len = $filesystem_types{$type}->{label_len};
- my $label;
- use bytes; # string lengths are in bytes
-
- if (defined($bdev_map{$bdev}->{path})) {
- # Convert path/type to label; prepend hostname if possible;
- # append numeric suffix if necessary.
-
- my $base;
- if ($bdev_map{$bdev}->{path} =~ m|^/|) {
- $base = $bdev_map{$bdev}->{path};
- } else {
- $base = $bdev_map{$bdev}->{type};
- }
- $base =~ s/[^\w]+/-/g;
- $base =~ s/^-//g;
- $base =~ s/-$//g;
-
- my $n = 0;
- my $suffix = '';
- do {
- $label = "$hostname-$base$suffix";
- if (length($label) > $label_len) {
- $label = substr($base, 0, $label_len - length($suffix))
- . $suffix;
- }
- $n++;
- $suffix = "-$n";
- } while (exists($id_map{"LABEL=$label"}));
- } else {
- my $n = 0;
- my $suffix;
- do {
- $n++;
- $suffix = "-$n";
- $label = substr($hostname, 0, $label_len - length($suffix))
- . $suffix;
- } while (exists($id_map{"LABEL=$label"}));
- }
-
- add_tag($bdev, 'LABEL', $label, 1);
- }
- }
-
- sub set_new_ids {
- for my $bdev (keys(%bdev_map)) {
- my $bdev_info = $bdev_map{$bdev};
- if ($bdev_info->{new_id}) {
- my ($name, $value) = split(/=/, $bdev_info->{new_id}, 2);
- my $setter;
- if ($name eq 'UUID') {
- $setter = $filesystem_types{$bdev_info->{type}}->{set_uuid};
- } elsif ($name eq 'LABEL') {
- $setter = $filesystem_types{$bdev_info->{type}}->{set_label};
- }
- defined($setter) or die "internal error: invalid new_id type";
- &{$setter}($bdev, $value);
- }
- }
- }
-
- sub update_config {
- my $map = shift;
-
- for my $match (@_) {
- # Generate a new config
- my $path = $match->{config}->{path};
- my $old = new FileHandle($path, 'r') or die "$!";
- my $new = new FileHandle("$path.new", POSIX::O_WRONLY | POSIX::O_CREAT,
- 0600)
- or die "$!";
- &{$match->{config}->{update}}($old, $new, $map);
- $old->close();
- $new->close();
-
- # New config should have same permissions as the old
- my (undef, undef, $mode, undef, $uid, $gid) = stat($path) or die "$!";
- chown($uid, $gid, "$path.new") or die "$!";
- chmod($mode & 07777, "$path.new") or die "$!";
-
- # Back up the old config and replace with the new
- my $old_path = $path . ($match->{config}->{suffix} || '.old');
- unlink($old_path);
- link($path, $old_path) or die "$!";
- rename("$path.new", $path) or die "$!";
-
- # If the package is installed, run the post-update function.
- # If the package is only unpacked, assume that its own postinst
- # will cover this.
- if ($match->{installed} && $match->{config}->{post_update}) {
- &{$match->{config}->{post_update}}();
- }
- }
- }
-
- sub update_all {
- # The update process may be aborted if a command fails, but we now
- # want to recover and ask the user what to do. We can use 'do' to
- # prevent 'die' from exiting the process, but we also need to
- # capture and present error messages using debconf as they may
- # otherwise be hidden. Therefore, we fork and capture stdout and
- # stderr from the update process in the main process.
- my $pid = open(PIPE, '-|');
- return (-1, '') unless defined $pid;
-
- if ($pid == 0) {
- # Complete redirection
- # </dev/null
- POSIX::close(0);
- POSIX::open('/dev/null', POSIX::O_RDONLY) or die "$!";
- # 2>&1
- POSIX::dup2(1, 2) or die "$!";
-
- # Do the update
- set_new_ids();
- update_config(@_);
- exit;
- } else {
- my @output = ();
- while (<PIPE>) {
- push @output, $_;
- }
- close(PIPE);
- return ($?, join('', @output));
- }
- }
-
- sub transition {
- use Debconf::Client::ConfModule ':all';
-
- my $bdev_regex = shift;
-
- retry:
- %bdev_map = ();
- %id_map = ();
-
- my @found_configs = scan_config_files($bdev_regex);
- my @matched_configs = grep({$_->{needs_update}} @found_configs);
- my @auto_configs = grep({defined($_->{config}->{update})} @matched_configs);
- my $found_boot_loader =
- grep({$_->{config}->{is_boot_loader} && $_->{unpacked}} @found_configs);
- my %update_map = ();
-
- # We can skip all of this if we didn't find any configuration
- # files that need conversion and we found the configuration file
- # for an installed boot loader.
- if (!@matched_configs && $found_boot_loader) {
- return;
- }
-
- my ($question, $answer, $ret, $seen);
-
- $question = 'linux-base/disk-id-convert-auto';
- ($ret, $seen) = input('high', $question);
- if ($ret && $ret != 30) {
- die "Error setting debconf question $question: $seen";
- }
- ($ret, $seen) = go();
- if ($ret && $ret != 30) {
- die "Error asking debconf question $question: $seen";
- }
- ($ret, $answer) = get($question);
- die "Error retrieving answer for $question: $answer" if $ret;
-
- if (@auto_configs && $answer eq 'true') {
- scan_devices();
- assign_new_ids();
-
- # Construct the device ID update map
- for my $bdev (keys(%bdev_map)) {
- if (@{$bdev_map{$bdev}->{ids}}) {
- $update_map{$bdev} = $bdev_map{$bdev}->{ids}->[0];
- }
- }
-
- # Weed out configurations which will be unaffected by this
- # mapping or by a custom mapping described in id_map_text.
- @auto_configs = grep({ defined($_->{id_map_text}) ||
- grep({exists($update_map{$_})}
- @{$_->{devices}}) }
- @auto_configs);
- }
-
- if (@auto_configs && $answer eq 'true') {
- if (grep({$bdev_map{$_}->{new_id}} keys(%bdev_map))) {
- $question = 'linux-base/disk-id-convert-plan';
- ($ret, $seen) = subst($question, 'relabel',
- join("\\n",
- map({sprintf("%s: %s",
- $_, $bdev_map{$_}->{new_id})}
- grep({$bdev_map{$_}->{new_id}}
- keys(%bdev_map)))));
- die "Error setting debconf substitutions in $question: $seen" if $ret;
- } else {
- $question = 'linux-base/disk-id-convert-plan-no-relabel';
- }
- ($ret, $seen) = subst($question, 'id_map',
- join("\\n",
- map({sprintf("%s: %s", $_, $update_map{$_})}
- keys(%update_map)),
- grep({defined}
- map({$_->{id_map_text}} @auto_configs))));
- die "Error setting debconf substitutions in $question: $seen" if $ret;
- ($ret, $seen) = subst($question, 'files',
- join(', ',
- map({$_->{config}->{path}} @auto_configs)));
- die "Error setting debconf substitutions in $question: $seen" if $ret;
- ($ret, $seen) = input('high', $question);
- if ($ret && $ret != 30) {
- die "Error setting debconf question $question: $seen";
- }
- ($ret, $seen) = go();
- if ($ret && $ret != 30) {
- die "Error asking debconf question $question: $seen";
- }
- ($ret, $answer) = get($question);
- die "Error retrieving answer for $question: $answer" if $ret;
-
- if ($answer eq 'true') {
- my ($rc, $output) = update_all(\%update_map, @auto_configs);
- if ($rc != 0) {
- # Display output of update commands
- $question = 'linux-base/disk-id-update-failed';
- $output =~ s/\n/\\n/g;
- ($ret, $seen) = subst($question, 'output', $output);
- die "Error setting debconf substitutions in $question: $seen"
- if $ret;
- ($ret, $seen) = input('high', $question);
- if ($ret && $ret != 30) {
- die "Error setting debconf question $question: $seen";
- }
- ($ret, $seen) = go();
- if ($ret && $ret != 30) {
- die "Error asking debconf question $question: $seen";
- }
-
- # Mark previous questions as unseen
- fset('linux-base/disk-id-convert-auto', 'seen', 'false');
- fset('linux-base/disk-id-convert-plan', 'seen', 'false');
- fset('linux-base/disk-id-convert-plan-no-relabel', 'seen',
- 'false');
- goto retry;
- }
- }
- }
-
- my @unconv_files = ();
- for my $match (@matched_configs) {
- if (!defined($match->{config}->{update})) {
- push @unconv_files, $match->{config}->{path};
- } else {
- my @unconv_bdevs = grep({!exists($update_map{$_})}
- @{$match->{devices}});
- if (@unconv_bdevs) {
- push @unconv_files, sprintf('%s: %s', $match->{config}->{path},
- join(', ',@unconv_bdevs));
- }
- }
- }
- if (@unconv_files) {
- $question = 'linux-base/disk-id-manual';
- ($ret, $seen) = subst($question, 'unconverted',
- join("\\n", @unconv_files));
- die "Error setting debconf substitutions in $question: $seen" if $ret;
- ($ret, $seen) = input('high', $question);
- if ($ret && $ret != 30) {
- die "Error setting debconf note $question: $seen";
- }
- ($ret, $seen) = go();
- if ($ret && $ret != 30) {
- die "Error showing debconf note $question: $seen";
- }
- }
-
- # Also note whether some (unknown) boot loader configuration file
- # must be manually converted.
- if (!$found_boot_loader) {
- $question = 'linux-base/disk-id-manual-boot-loader';
- ($ret, $seen) = input('high', $question);
- if ($ret && $ret != 30) {
- die "Error setting debconf note $question: $seen";
- }
- ($ret, $seen) = go();
- if ($ret && $ret != 30) {
- die "Error showing debconf note $question: $seen";
- }
- }
- }
-
- package DebianKernel::BootloaderConfig;
-
- my %default_bootloader = (amd64 => 'lilo',
- i386 => 'lilo',
- ia64 => 'elilo',
- s390 => 'zipl');
-
- sub check {
- use Debconf::Client::ConfModule ':all';
-
- my ($deb_arch) = @_;
-
- # Is there an historical 'default' boot loader for this architecture?
- my $loader_exec = $default_bootloader{$deb_arch};
- return unless defined($loader_exec);
-
- # Is the boot loader installed?
- my ($loaderloc) = grep(-x, map("$_/$loader_exec",
- map({ length($_) ? $_ : "." }
- split(/:/, $ENV{PATH}))));
- return unless defined($loaderloc);
-
- # Is do_bootloader explicitly set one way or the other?
- my $do_bootloader;
- if (my $conf = new FileHandle('/etc/kernel-img.conf', 'r')) {
- while (<$conf>) {
- $do_bootloader = 0 if /^\s*do_bootloader\s*=\s*(no|false|0)\s*$/i;
- $do_bootloader = 1 if /^\s*do_bootloader\s*=\s*(yes|true|1)\s*$/i;
- }
- $conf->close();
- }
- return if defined($do_bootloader);
-
- # Warn the user that do_bootloader is disabled by default.
- my ($question, $ret, $seen);
- $question = "linux-base/do-bootloader-default-changed";
- ($ret,$seen) = input('high', "$question");
- die "Error setting debconf question $question: $seen" if $ret && $ret != 30;
- ($ret,$seen) = go();
- die "Error asking debconf question $question: $seen" if $ret && $ret != 30;
- }
-
- package main;
-
- capb('escape');
-
- sub version_lessthan {
- my ($left, $right) = @_;
- return system('dpkg', '--compare-versions', $left, 'lt', $right) == 0;
- }
-
- # No upgrade work is necessary during a fresh system installation.
- # But since linux-base is a new dependency of linux-image-* and did
- # not exist until needed for the libata transition, we cannot simply
- # test whether this is a fresh installation of linux-base. Instead,
- # we test:
- # - does /etc/fstab exist yet (this won't even work without it), and
- # - are any linux-image-* packages installed yet?
- sub is_fresh_installation {
- if (-f '/etc/fstab') {
- for (`dpkg-query 2>/dev/null --showformat '\${status}\\n' -W 'linux-image-*'`) {
- return 0 if / installed\n$/;
- }
- }
- return 1;
- }
-
- my $deb_arch = `dpkg --print-architecture`;
- chomp $deb_arch;
-
- my $reconfigure = ($ARGV[0] eq 'reconfigure' ||
- defined($ENV{DEBCONF_RECONFIGURE}));
- if ($deb_arch ne 's390' && ($reconfigure || !is_fresh_installation())) {
- my @bdev_regex = ();
-
- my $libata_transition_ver =
- ($deb_arch eq 'i386' || $deb_arch eq 'amd64') ? '2.6.32-10' : '2.6.32-11';
- if ($reconfigure || version_lessthan($ARGV[1], $libata_transition_ver)) {
- # Match standard IDE and SCSI device names, plus wildcards
- # in disk device names to allow for mdadm insanity.
- push @bdev_regex, '[hs]d[a-z\?\*][\d\?\*]*$';
- push @bdev_regex, 's(?:cd|r)\d+$';
- }
-
- # hpsa took over some controllers from cciss in 2.6.37, so their
- # targets are also treated (and named) like SCSI devices now.
- if ($reconfigure || version_lessthan($ARGV[1], '3')) {
- push @bdev_regex, 'cciss/';
- push @bdev_regex, 'sd[a-z\?\*][\d\?\*]*$';
- }
-
- if (@bdev_regex) {
- DebianKernel::DiskId::transition('^/dev/(?:' .
- join('|', @bdev_regex) . ')');
- }
- }
-
- if (!is_fresh_installation() && version_lessthan($ARGV[1], '2.6.32-18')) {
- DebianKernel::BootloaderConfig::check($deb_arch);
- }
-
- exec("set -e\nset -- @ARGV\n" . << 'EOF');
-
- EOF
-